package com.example.demo.util.excel;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ExcelWorkSheetHandler<T> implements XSSFSheetXMLHandler.SheetContentsHandler {

    private static final Log LOG = LogFactory.getLog(ExcelWorkSheetHandler.class);


    private final static String HEADER_KEY = "HEADER";
    private boolean verifyHeader = true;
    private int skipRows = 0;
    private int HEADER_ROW = 0;
    private int currentRow = 0;
    private List<T> valueList;
    private Class<T> type;
    private Map<String, String> cellMapping = null;
    private T objCurrentRow = null;
    private T objHeader = null;


    public ExcelWorkSheetHandler(Class<T> type, Map<String, String> cellMapping) {
        this.type = type;
        this.cellMapping = cellMapping;
        this.valueList = new ArrayList<T>();
    }


    public ExcelWorkSheetHandler(Class<T> type, Map<String, String> cellMapping, int skipRows) {
        this.type = type;
        this.cellMapping = cellMapping;
        this.valueList = new ArrayList<T>();
        this.skipRows = skipRows;
    }


    public List<T> getValueList() {
        return valueList;
    }


    public boolean isVerifyHeader() {
        return verifyHeader;
    }


    public void setVerifyHeader(boolean verifyHeader) {
        this.verifyHeader = verifyHeader;
    }


    @Override
    public void startRow(int rowNum) {
        this.currentRow = rowNum;

        if (verifyHeader) {
            objHeader = this.getInstance();
        }
        //跳过第一行
        if (rowNum > HEADER_ROW && rowNum >= skipRows) {
            objCurrentRow = this.getInstance();
        }
    }


    @Override
    public void cell(String cellReference, String formattedValue) {

        if (currentRow > skipRows) {
            if (StringUtils.isEmpty(formattedValue)) {
                return;
            }

            if (HEADER_ROW == currentRow && verifyHeader) {
                this.assignValue(objHeader, getCellReference(cellReference), formattedValue);
            }

            this.assignValue(objCurrentRow, getCellReference(cellReference), formattedValue);

        }
    }


    @Override
    public void endRow() {

        if (currentRow > skipRows) {
            if (null != objCurrentRow && isObjectHasValue(objCurrentRow)) {

                this.valueList.add(objCurrentRow);
            }

            objCurrentRow = null;
        }
    }


    @Override
    public void headerFooter(String text, boolean isHeader, String tagName) {

    }

    private String getCellReference(String cellReference) {
        if (StringUtils.isEmpty(cellReference)) {
            return "";
        }

        return cellReference.split("[0-9]*$")[0];
    }

    private void assignValue(Object targetObj, String cellReference, String value) {
        if (null == targetObj || StringUtils.isEmpty(cellReference) || StringUtils.isEmpty(value)) {
            return;
        }

        try {
            String propertyName = this.cellMapping.get(cellReference);
            if (null == propertyName) {
                propertyName = this.cellMapping.get(cellReference.toLowerCase());
            }
            if (null != propertyName) {
                if (targetObj instanceof String) {
                    Field field = targetObj.getClass().getDeclaredField(propertyName);
                    field.setAccessible(true);
                    field.set(targetObj, value.toCharArray());
                } else {
                    Object object = getRealTypeValue(PropertyUtils.getProperty(targetObj, propertyName), value);
                    PropertyUtils.setSimpleProperty(targetObj, propertyName, object);
                }

            }
        } catch (IllegalAccessException iae) {
            LOG.error(iae.getMessage());
        } catch (InvocationTargetException ite) {
            LOG.error(ite.getMessage());
        } catch (NoSuchMethodException nsme) {
            LOG.error(nsme.getMessage());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }


    private Object getRealTypeValue(Object object, String v) {

        if (object instanceof String) {
            return v;
        } else if (object instanceof Integer) {
            return Integer.valueOf(v);
        } else if (object instanceof Long) {
            return Long.valueOf(v);
        } else if (object instanceof Double) {
            return Double.valueOf(v);
        } else if (object instanceof Float) {
            return Float.valueOf(v);
        } else if (object instanceof Byte) {
            return Byte.valueOf(v);
        } else if (object instanceof Short) {
            return Short.valueOf(v);
        } else if (object instanceof Boolean) {
            return Boolean.valueOf(v);
        }

        return v;
    }

    private T getInstance() {
        try {
            return type.newInstance();
        } catch (InstantiationException ie) {
            LOG.error(ie.getMessage());
        } catch (IllegalAccessException iae) {
            LOG.error(iae.getMessage());
        }
        return null;
    }


    private boolean isObjectHasValue(Object targetObj) {
        for (Map.Entry<String, String> entry : cellMapping.entrySet()) {
            if (!StringUtils.equalsIgnoreCase(HEADER_KEY, entry.getKey())) {
                if (StringUtils.isNotBlank(getPropertyValue(targetObj, entry.getValue()))) {
                    return true;
                }
            }
        }
        return false;
    }


    private String getPropertyValue(Object targetObj, String propertyName) {
        String value = "";
        if (null == targetObj || StringUtils.isBlank(propertyName)) {
            return value;
        }
        if (targetObj instanceof String)
            return (String) targetObj;
        try {
            if (PropertyUtils.isReadable(targetObj, propertyName)) {
                Object v = PropertyUtils.getSimpleProperty(targetObj, propertyName);
                if (null != v && StringUtils.isNotBlank(v.toString())) {
                    value = v.toString();
                }
            } else {

            }
        } catch (IllegalAccessException iae) {

        } catch (InvocationTargetException ite) {

        } catch (NoSuchMethodException nsme) {

        }
        return value;
    }
}
